#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define Convert(addrptr) ((struct sockaddr *)addrptr)

namespace network
{
    const static int defaultsockfd = -1; // 默认socket文件描述符
    const int backlog = 5; // 最大连接请求数

    enum // 错误类型
    {
        SocketError = 1,
        BindError,
        ListenError,
        SendError,
    };

    // 封装Socket接口类（设计模式：模板方法类）
    class Socket
    {
    public:
         virtual ~Socket() {}
        virtual void CreateSocketOrDie() = 0;
        virtual void BindSocketOrDie(uint16_t port) = 0;
        virtual void ListenSocketOrDie(int backlog) = 0;
        virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
        virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0;
        virtual int GetSockFd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual void CloseSocket() = 0;
        virtual bool Recv(std::string *buffer, int size) = 0;
        virtual void Send(std::string &send_str) = 0;
        virtual void ReUseAddr() = 0;
    public:
        // 建立监听套接字
        void BuildListenSocketMethod(uint16_t port, int backlog)
        {
            CreateSocketOrDie();
            ReUseAddr();
            BindSocketOrDie(port);
            ListenSocketOrDie(backlog);
        }
        // 连接服务器
        bool BuildConnectSocketMethod(std::string &serverip, uint16_t serverport)
        {
            CreateSocketOrDie();
            return ConnectServer(serverip, serverport);
        }

        // 建立普通套接字
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd) {}
        ~TcpSocket() { 
            // CloseSocket(); 
        }

        void CreateSocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if(_sockfd < 0)
            {
                std::cerr << "socket create error" << std::endl;
                exit(SocketError);
            }
        }

        void BindSocketOrDie(uint16_t port) override
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = htonl(INADDR_ANY);

            if(bind(_sockfd, Convert(&addr), sizeof(addr) < 0)) {                
                std::cerr << "bind error" << std::endl;
                exit(SocketError);
            }
        }

        void ListenSocketOrDie(int backlog) override
        {
            if(listen(_sockfd, backlog) < 0) {
                std::cerr << "listen error" << std::endl;
                exit(SocketError);
            }
        }

        Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int newfd = accept(_sockfd, Convert(&peer), &len);
            if(newfd < 0) {
                std::cerr << "accept error" << std::endl;
                exit(SocketError);
            }
            
            *peerport = ntohs(peer.sin_port);
            *peerip = inet_ntoa(peer.sin_addr);

            return new TcpSocket(newfd); // -> Socket *socket = new TcpSocket(newfd);
        }

        bool ConnectServer(std::string &serverip, uint16_t serverport) override
        {
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_port = htons(serverport);
            server.sin_addr.s_addr = inet_addr(serverip.c_str());

            if(connect(_sockfd, Convert(&server), sizeof(server) < 0)) {
                std::cerr << "connect error" << std::endl;
                return false;
            }

            return true;
        }

        int GetSockFd() override
        {
            return _sockfd;
        }

        void SetSockFd(int sockfd) override
        {
            _sockfd = sockfd;
        }

        void CloseSocket() override
        {
            if(_sockfd > defaultsockfd) {
                close(_sockfd);
            }
        }

        bool Recv(std::string *buffer, int size) override
        {
            char recvBuffer[size];
            int recvLen = recv(_sockfd, recvBuffer, size, 0);
            if(recvLen < 0) {
                std::cerr << "recv error" << std::endl;
                return false;
            } else if(recvLen == 0) {
                return false;
            }

            recvBuffer[recvLen] = '\0';
            *buffer += recvBuffer;
            return true;
        }

        void Send(std::string &send_str) override
        {
            if(send(_sockfd, send_str.c_str(), send_str.size(), 0))
            {
                std::cerr << "send error" << std::endl;
                exit(SendError);
            }
        }

        void ReUseAddr() override
        {
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        }

    private:
        int _sockfd;
    };
}